﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Reflection;
using System.Text;
using System.Xml;
using Common.Utilities.DLR;

namespace Workflows.Components.TextMining.Collapse
{
    [Serializable][OrmClass("CollapsedEntity", "CollapsedEntityID", true)]
    public class CollapsedEntity
    {
        #region fields
        private Type _EntityType;
        private Dictionary<string, MemberInfo> _Members;
        private int _CollapsedKey;
        private List<int> _EntityKeys;
        private double _Score;
        private double _Confidence;
        #endregion

        public CollapsedEntity(Type entityType)
        {
            this._EntityType = entityType;
            this._CollapsedKey = 0;
            this._EntityKeys = new List<int>();
            this._Score = 0;
            this._Confidence = 0;
            this._Members = this.GetEvaluableMembersForType(this._EntityType);
        }

        public CollapsedEntity(Type entityType,string connStr, int collapsedEntityID)
        {
            this._EntityType = entityType;
            this._Members = this.GetEvaluableMembersForType(this._EntityType);
            this.LoadFromDB(connStr, collapsedEntityID);
        }

        private Dictionary<string,MemberInfo> GetEvaluableMembersForType(Type type)
        {
            Dictionary<string,MemberInfo> members=new Dictionary<string, MemberInfo>();
            FieldInfo[] fields = type.GetFields();
            if(fields !=null && fields.Length>0)
            {
                foreach(FieldInfo field in fields)
                {
                    object[] fieldAtts = field.GetCustomAttributes(typeof (EvaluableFieldAttribute), false);
                    if( fieldAtts !=null && fieldAtts.Length==1)
                    {
                        members.Add(field.Name,field);
                    }
                }
            }
            PropertyInfo[] props = type.GetProperties();
            if(props !=null && props.Length>0)
            {
                foreach(PropertyInfo prop in props)
                {
                    object[] propAtts = prop.GetCustomAttributes(typeof (EvaluableFieldAttribute), false);
                    if(propAtts !=null && propAtts.Length ==1)
                    {
                        members.Add(prop.Name,prop);
                    }
                }
            }
            return members;
        }

        #region props
        [OrmField("EntityType",SqlDbType.Text,2000, OrmFieldSerializationMethod.BinarySerialization)]
        public Type EntityType
        {
            get
            {
                return this._EntityType;
            }
        }

        public int CollapsedEntityID
        {
            get { return _CollapsedKey; }
            set { _CollapsedKey = value; }
        }
        [OrmField("EntityKeys",SqlDbType.Text,2000,OrmFieldSerializationMethod.BinarySerialization)]
        public List<int> EntityKeys
        {
            get { return _EntityKeys; }
            set { _EntityKeys = value; }
        }

        public double Score
        {
            get { return _Score; }
            set { _Score = value; }
        }

        public double Confidence
        {
            get { return _Confidence; }
            set { _Confidence = value; }
        }
        #endregion

        public bool TryCollapse(CollapsibleEntitySettings settings, object instance)
        {
            if(instance !=null && instance.GetType()==this._EntityType)
            {
                MemberInfo keyMember = this._EntityType.GetMember(settings.ClassAttr.KeyName)[0];
                int keyValue2 = 0;
                if(keyMember.MemberType==MemberTypes.Field)
                {
                    keyValue2 = (int)((FieldInfo)keyMember).GetValue(instance);
                }
                else if(keyMember.MemberType==MemberTypes.Property)
                {
                    keyValue2 = (int)((PropertyInfo)keyMember).GetValue(instance, null);
                }
                double collapseScore = 0;
                if(this._EntityKeys==null || this._EntityKeys.Count ==0)
                {
                    this._EntityKeys.Add(keyValue2);
                    this._Score = 0;
                    return true;
                }

                Dictionary<int,object> entityInstances=new Dictionary<int, object>();
                foreach(int id in this._EntityKeys)
                {
                    entityInstances.Add(id, settings.GetEntityByKey(id));
                }
                if(settings.ClassAttr.MaximizeCombinedFieldScore)
                {
                    foreach(string memberName in this._Members.Keys)
                    {
                        double maxFieldScore = 0;
                        EvaluableFieldAttribute evaluableField = settings.FieldAttrs[memberName];
                        IFieldEvaluator evaluator = evaluableField.Evaluator;
                        MemberInfo member = this._Members[memberName];
                        object memberValue1 = null;
                        object memberValue2 = null;
                        if(member.MemberType==MemberTypes.Field)
                        {
                            memberValue2 = ((FieldInfo) member).GetValue(instance);
                            foreach(int key in this._EntityKeys)
                            {
                                memberValue1 = ((FieldInfo) member).GetValue(entityInstances[key]);
                                double score = evaluator.CalculateSimilarity(evaluableField, memberValue1, memberValue2);
                                maxFieldScore = Math.Max(maxFieldScore, score);
                            }
                        }
                        else if(member.MemberType==MemberTypes.Property)
                        {
                            memberValue2 = ((PropertyInfo)member).GetValue(instance,null);
                            foreach (int key in this._EntityKeys)
                            {
                                memberValue1 = ((PropertyInfo)member).GetValue(entityInstances[key], null);
                                double score = evaluator.CalculateSimilarity(evaluableField, memberValue1, memberValue2);
                                maxFieldScore = Math.Max(maxFieldScore, score);
                            }
                        }
                        collapseScore += maxFieldScore;
                    }
                }
                else
                {
                    foreach(int id in this._EntityKeys)
                    {
                        double instanceScore = 0;
                        object instance1 = entityInstances[id];
                        foreach(string memberName in this._Members.Keys)
                        {
                            object memberValue1 = null;
                            object memberValue2 = null;
                            MemberInfo member = this._Members[memberName];
                            if(member.MemberType==MemberTypes.Field)
                            {
                                memberValue1 = ((FieldInfo) member).GetValue(instance1);
                                memberValue2 = ((FieldInfo)member).GetValue(instance);
                            }
                            else if(member.MemberType==MemberTypes.Property)
                            {
                                memberValue1 = ((PropertyInfo)member).GetValue(instance1,null);
                                memberValue2 = ((PropertyInfo)member).GetValue(instance,null);
                            }
                            EvaluableFieldAttribute evaluableField = settings.FieldAttrs[memberName];
                            IFieldEvaluator evaluator = evaluableField.Evaluator;
                            double fieldScore = evaluator.CalculateSimilarity(evaluableField, memberValue1, memberValue2);
                            instanceScore += fieldScore;
                        }
                        collapseScore = Math.Max(collapseScore, instanceScore);
                    }
                }
                if(collapseScore>=settings.ClassAttr.CollapseCutoffScore)
                {
                    if (this._Score == 0)
                        this._Score = collapseScore;
                    else
                        this._Score = Math.Min(this._Score, collapseScore);

                    this._EntityKeys.Add(keyValue2);
                    return true;
                }
                else
                {
                    return false;
                }
            }
            else
            {
                throw new Exception("Object not initialized or of wrong type");
            }
        }

        private void LoadFromDB(string connStr, int collapsedID)
        {
            ORMapping<CollapsedEntity> orm=new ORMapping<CollapsedEntity>();
            CollapsedEntity collapsedEntity = orm.GetObjByKey(connStr, collapsedID.ToString());
            this._CollapsedKey = collapsedEntity.CollapsedEntityID;
            this._EntityKeys = collapsedEntity.EntityKeys;
            this._Confidence = collapsedEntity.Confidence;
            this._Score = collapsedEntity.Score;
        }

        public void SaveToDB(string connStr)
        {
            ORMapping<CollapsedEntity> orm = new ORMapping<CollapsedEntity>();
            CollapsedEntity collapsedEntity=new CollapsedEntity(this._EntityType);
            collapsedEntity.CollapsedEntityID = this.CollapsedEntityID;
            collapsedEntity.EntityKeys = this.EntityKeys;
            collapsedEntity.Confidence = this.Confidence;
            collapsedEntity.Score = this.Score;
            orm.Update(connStr, ref collapsedEntity);
            this._CollapsedKey = collapsedEntity.CollapsedEntityID;
        }
    }
}
